﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Somnus.Core.SceneManager;
using FarseerPhysics;
using FarseerPhysics.DebugViews;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics.Collision;
using Somnus.Utils;
using Somnus.Objects;
using FarseerPhysics.Common;

namespace Somnus.Core
{
    class DreamWorld : World 
    {
        private float _scaleFactor;
        private List<PhysicsObject> _objectList;
        private List<Layer> _layerList;
        private DebugViewXNA _debugView;
        public Camera2d camera;
        public Viewport viewport;
        public Boolean enableDebugView;

        public DreamWorld(Vector2 gravity, float scaleFactor)
            : base(gravity)
        {
            _scaleFactor = scaleFactor;

            _objectList = new List<PhysicsObject>();
            camera = new Camera2d(viewport);
            camera.Limits = new Rectangle(0, 0, 8000, 600);
            _debugView = new DebugViewXNA(this);
            _debugView.AppendFlags(DebugViewFlags.DebugPanel);
            _debugView.DefaultShapeColor = Color.Purple;
            _debugView.SleepingShapeColor = Color.Pink;
            _debugView.LoadContent(Game1.getInstance().GraphicsDevice, Game1.getInstance().Content);

            enableDebugView = false;

            _layerList = new List<Layer>
            {
                new Layer(camera) { Parallax = new Vector2(0.0f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.1f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.2f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.3f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.4f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.5f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.6f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(0.8f, 1.0f) },
                new Layer(camera) { Parallax = new Vector2(1.0f, 1.0f) }
            };
        }

        public float ScaleFactor
        {
            get
            {
                return _scaleFactor;
            }
        }

        public void addBG(int k, string textura)
        {
            _layerList[k].Bgs.Add(new Background(this, Game1.getInstance().Content.Load<Texture2D>(textura)));
        }

        public void addObject(PhysicsObject physicsObject)
        {
            _objectList.Add(physicsObject);
        }

        public void update(GameTime gameTime)
        {
            for (int i = 0; i < _objectList.Count; i++)
            {
                _objectList[i].update(gameTime);
            }

            if (Controller.KeyPressed(Keys.F1))
            {
                enableDebugView = !enableDebugView;
            }
        }

        public void draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            spriteBatch.End();

            // Draw de layers + parallax

            foreach (Layer layer in _layerList)
            {
                layer.Draw(gameTime, spriteBatch);
            }

            spriteBatch.Begin(SpriteSortMode.FrontToBack,
                        BlendState.AlphaBlend,
                        null,
                        null,
                        null,
                        null,
                        camera.get_transformation(Game1.getInstance().graphics));

            for (int i = 0; i < _objectList.Count; i++)
            {
                _objectList[i].draw(gameTime, spriteBatch);
            }

            
            spriteBatch.End();

            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);

            if (enableDebugView)
            {
                Matrix projection = Matrix.CreateOrthographicOffCenter(0f, Game1.getInstance().GraphicsDevice.Viewport.Width / ScaleFactor,
                                                                 Game1.getInstance().GraphicsDevice.Viewport.Height / ScaleFactor, 0f, 0f,
                                                                 1f);
                Vector2 _screenCenter = new Vector2(Game1.getInstance().GraphicsDevice.Viewport.Width / 2f,
                                                    Game1.getInstance().GraphicsDevice.Viewport.Height / 2f);

                Matrix view = Matrix.CreateTranslation(new Vector3(-camera.Pos.X / (ScaleFactor), -camera.Pos.Y / ScaleFactor, 0f))
                                * Matrix.CreateTranslation(new Vector3((_screenCenter / ScaleFactor), 0f))
                                * Matrix.CreateTranslation(new Vector3(camera.offsetX / ScaleFactor, camera.offsetY / ScaleFactor, 0f));

                _debugView.RenderDebugData(ref projection, ref view);
            }
        }

        public PhysicsObject getObjectByName(String name)
        {
            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i].Name == name)
                    return _objectList[i];
            }
            return null;
        }

        public PhysicsObject getObjectByType(Type type)
        {

            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i].GetType() == type)
                    return _objectList[i];
            }
            return null;
        }

        public List<PhysicsObject> getObjectsByType(Type type)
        {
            List<PhysicsObject> listByType = new List<PhysicsObject>();

            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i] is PhysicsObject)
                    listByType.Add(_objectList[i]);
            }
            return listByType;
        }
    }
}
